#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <unordered_set>

// 1.主题管理者 -- 维护(连接, 订阅者)的关系 + (主题名称, 主题)的关系
// 2.封装 -- 主题 + 订阅者 
//      - 订阅者 : 1.内含对应连接 + 订阅的主题名称 2.添加/删除主题名称
//      - 主题 : 1.内含主题名称 + 订阅该主题的人  2.添加/删除订阅者 +  推送主题消息
// 3.响应(dispatcher) -- 对请求的响应, 对连接关闭的响应

namespace jsonrpc
{
    namespace server
    {
        class TopicManager
        {
        public:
            using ptr = std::shared_ptr<TopicManager>;
            TopicManager(){}
        public:
            // 1.收到请求的处理
            void onTopicRequest(const BaseConnection::ptr &conn, const TopicRequest::ptr &msg)
            {
                TopicOptype topic_optype = msg->optype();
                bool ret = true;
                switch(topic_optype){
                    //主题的创建
                    case TopicOptype::TOPIC_CREATE: topicCreate(conn, msg); break;
                    //主题的删除
                    case TopicOptype::TOPIC_REMOVE: topicRemove(conn, msg); break;
                    //主题的订阅
                    case TopicOptype::TOPIC_SUBSCRIBE: ret = topicSubscribe(conn, msg); break;
                    //主题的取消订阅
                    case TopicOptype::TOPIC_CANCEL: topicCancel(conn, msg); break;
                    //主题消息的发布
                    case TopicOptype::TOPIC_PUBLISH: ret = topicPublish(conn, msg); break;
                    default:  return errorResponse(conn, msg, RCode::RCODE_INVALID_OPTYPE);
                }
                if (!ret) return errorResponse(conn, msg, RCode::RCODE_NOT_FOUND_TOPIC);
                return topicResponse(conn, msg);
            }

            // 2.连接关闭的处理(订阅者 or 发布者)
            void onShutdown(const BaseConnection::ptr &conn)
            {
                // 订阅者 -- 对应主题删除该订阅者 + 维护连接关系
                Subscriber::ptr subscriber;
                std::vector<Topic::ptr> topics; 
                {
                    // 1.找到订阅者
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto sub_it = _subscribers.find(conn);
                    if(sub_it == _subscribers.end())return; //是发布者就不处理了
                    subscriber = sub_it->second;
                    // 2.找到订阅的主题
                    for(auto& topic_name : subscriber->topics)
                    {
                        auto topic_it = _topics.find(topic_name);// 根据主题名 找 主题
                        if(topic_it == _topics.end())continue;
                        topics.push_back(topic_it->second);
                    }
                    // 4.主题管理者删除订阅者
                    _subscribers.erase(sub_it);
                }

                // 3.对应的主题删掉订阅者
                for(auto& topic : topics)
                {
                    topic->removeSubscriber(subscriber);
                }
            }

        private:
            // 请求的响应
            void errorResponse(const BaseConnection::ptr &conn, const TopicRequest::ptr &msg, RCode rcode)
            {
                auto msg_rsp = MessageFactory::create<TopicResponse>();
                msg_rsp->setId(msg->rid());
                msg_rsp->setMType(MType::RSP_TOPIC);
                msg_rsp->setRCode(rcode);
                conn->send(msg_rsp);
            }

            void topicResponse(const BaseConnection::ptr &conn, const TopicRequest::ptr &msg)
            {
                auto msg_rsp = MessageFactory::create<TopicResponse>();
                msg_rsp->setId(msg->rid());
                msg_rsp->setMType(MType::RSP_TOPIC);
                msg_rsp->setRCode(RCode::RCODE_OK);
                conn->send(msg_rsp);
            }

        private:
            // 创建主题
            void topicCreate(const BaseConnection::ptr &conn, const TopicRequest::ptr &msg)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                // 构造主题对象, 添加映射关系的管理
                std::string topic_name = msg->topicKey();
                auto topic = std::make_shared<Topic>(topic_name);
                _topics.insert(std::make_pair(topic_name, topic));
            }
            // 删除主题
            void topicRemove(const BaseConnection::ptr &conn, const TopicRequest::ptr &msg)
            {
                // 1.找到主题, 找到该主题的订阅者(可能有多个) 2.订阅者删掉主题 + 主题管理者去掉建立的关系
                std::string topic_name = msg->topicKey();
                std::unordered_set<Subscriber::ptr> subscribers; // 该主题可能有多个订阅者
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _topics.find(topic_name); // 拿到Topic -- 里面有订阅该主题的订阅者
                    if (it == _topics.end())
                    {
                        return;
                    }
                    subscribers = it->second->subscribers;
                    _topics.erase(it); // 删除主题数据
                }
                // 订阅者删除主题
                for (auto subscriber : subscribers)
                {
                    subscriber->removeTopic(topic_name);
                }
            }

            // 订阅主题
            bool topicSubscribe(const BaseConnection::ptr &conn, const TopicRequest::ptr &msg)
            {
                // 1.找到主题对象 + 订阅者
                Topic::ptr topic;
                Subscriber::ptr subscriber;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto topic_it = _topics.find(msg->topicKey());
                    if (topic_it == _topics.end())
                    {
                        // 没有该主题
                        return false;
                    }
                    else
                        topic = topic_it->second; // 主题

                    auto sub_it = _subscribers.find(conn);
                    if (sub_it == _subscribers.end())
                    {
                        // 是新连接, 创建订阅者
                        subscriber = std::make_shared<Subscriber>(conn);
                        _subscribers.insert(std::make_pair(conn, subscriber));
                    }
                    else
                    {
                        subscriber = sub_it->second; // 订阅者
                    }
                }
                // 2.主题添加订阅者, 订阅者添加主题 + 做好连接关系的维护
                topic->appendSubscriber(subscriber);
                subscriber->appendTopic(msg->topicKey());
                return true;
            }

            // 取消订阅
            void topicCancel(const BaseConnection::ptr &conn, const TopicRequest::ptr &msg)
            {
                // 1.找到主题, 找到订阅者 2.主题去掉订阅者, 订阅者去掉主题
                Topic::ptr topic;
                Subscriber::ptr subscriber;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto topic_it = _topics.find(msg->topicKey());
                    if (topic_it != _topics.end())
                    {
                        topic = topic_it->second; // 主题
                    }

                    auto sub_it = _subscribers.find(conn);
                    if (sub_it != _subscribers.end())
                    {
                        subscriber = sub_it->second; // 订阅者
                    }
                }
                if (topic)
                    topic->removeSubscriber(subscriber);
                if (subscriber)
                    subscriber->removeTopic(msg->topicKey());
            }
            // 主题消息的发布
            bool topicPublish(const BaseConnection::ptr &conn, const TopicRequest::ptr &msg)
            {
                // 找到主题, 进行消息通知
                Topic::ptr topic;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto topic_it = _topics.find(msg->topicKey());
                    if (topic_it == _topics.end())
                    {
                        return false;
                    }
                    topic = topic_it->second;
                }
                topic->PushMessage(msg);
                return true;
            }

        private:
            // 封装订阅者
            struct Subscriber
            {
                using ptr = std::shared_ptr<Subscriber>;
                std::mutex _mutex;
                BaseConnection::ptr conn;
                std::unordered_set<std::string> topics; // 订阅者所订阅的 主题名称
            public:
                Subscriber(const BaseConnection::ptr &con) : conn(con) {}

            public:
                // 新增订阅
                void appendTopic(const std::string &topic_name)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    topics.insert(topic_name);
                }
                // 主题被删除 or 取消订阅
                void removeTopic(const std::string &topic_name)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    topics.erase(topic_name);
                }
            };
            // 封装主题
            struct Topic
            {
                using ptr = std::shared_ptr<Topic>;
                std::mutex _mutex;
                std::string topic_name;
                std::unordered_set<Subscriber::ptr> subscribers; // 当前主题的 订阅者
            public:
                Topic(const std::string &tpname) : topic_name(tpname) {}

            public:
                // 添加订阅者
                void appendSubscriber(const Subscriber::ptr &sub)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    subscribers.insert(sub);
                }
                // 移除订阅者
                void removeSubscriber(const Subscriber::ptr &sub)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    subscribers.erase(sub);
                }
                // 推送消息
                void PushMessage(const BaseMessage::ptr &msg)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    // 找到所有订阅者, 推送消息
                    for (auto subscriber : subscribers)
                    {
                        subscriber->conn->send(msg);
                    }
                }
            };

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, Topic::ptr> _topics;                   // 主题名称 -- 主题
            std::unordered_map<BaseConnection::ptr, Subscriber::ptr> _subscribers; // 连接 -- 订阅者
        };
    }
}